package team3.tracking.services;


import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
import java.util.Date;

import team3.tracking.P4_ClientTrackingActivity;
import team3.tracking.util.ClientData;
import team3.tracking.util.PositionInfo;
import team3.tracking.util.SDCard;
import team3.tracking.util.Vector;
import android.content.Context;
import android.content.Intent;
import android.location.Location;
import android.location.LocationListener;
import android.location.LocationManager;
import android.os.IBinder;
import android.os.RemoteException;
import android.telephony.TelephonyManager;


public class LocationUpdateAndQuery extends ServiceGPS{

	protected double ratio;
	
    protected String packageName = "team3.tracking.services";
    protected String fileName = "ClientSideTrack.txt"; //Override the original filename of ServiceGPS(!)
    protected String fileName2 = "ServerSideTrack.txt";
    protected SDCard serverSDCard;
    protected final int interval = 1000; //1 second, Override the original interval value of ServiceGPS(!)
    private final int THRESHOLD = 20; //meter walked in any other direction different from the predicted
	private final double MAX_WALKING_SPEED = 5; //meter per second
    protected final int port = 6000; //UDP Port
	protected final String address = "141.58.71.140";//"141.58.69.56"; //the IP should be change to the actual IP server!!
	protected UDPHandler mUDPHandler;
	protected String deviceId;
	protected double latitudPredicted;
	protected double longitudPredicted;	
	protected double lastLatitudPredicted;
	protected double lastLongitudPredicted;
	protected Location lastLocation2;
	protected Long numberSentUpdates;
	protected Long totalUpdates;
	protected LocationListener subscriber;
	protected LocationManager locationManager;
	final static int TCPServerPortID = 8000;
	TelephonyManager telephonyManager; 
	Socket TCPClientSocket;
	OutputStream TCPOutStream;
	InputStream TCPInStream;
	byte [] TCPByteStream;
	TCPQueryThread TCPGet;
	boolean queryAnswered;
	private double remotelatitude;;
	private double remoteLongitude;
	Vector vectorPredicted;
	
    
    public void onCreate() { 
		super.onCreate();
		//Get the Device ID
		telephonyManager = (TelephonyManager)getSystemService(Context.TELEPHONY_SERVICE);
		deviceId = telephonyManager.getDeviceId();
		
		vectorPredicted = new Vector(0,0);
		lastLocation2 = null;
		latitudPredicted = 0;
		longitudPredicted= 0;
		lastLatitudPredicted= 0;
		lastLongitudPredicted= 0;
		numberSentUpdates = (long) 0;
		totalUpdates= (long) 0;
		ratio = 0;
		
		subscriber = new UpdatedSubscriber();
		//Setting for GPS or WIFI use
		locationManager = (LocationManager)
        		getSystemService(Context.LOCATION_SERVICE);
		locationManager.requestLocationUpdates( //Registers the current activity to be notified periodically
    			LocationManager.GPS_PROVIDER,
        		//LocationManager.NETWORK_PROVIDER,
    			interval,
    			minDistance,
    			subscriber);		
		
		mUDPHandler = new UDPHandler(address,port);
		mUDPHandler.start();
		
        serverSDCard = new SDCard(packageName,fileName2);
        serverSDCard.write(headInfo);
        updateUI();
	}  
    
    public void onDestroy(){
    	super.onDestroy();
    	System.out.println("service LocationUpdateAndQuery destroyed");
    	mUDPHandler.close();
    	serverSDCard.append(footInfo);
    	locationManager.removeUpdates(subscriber);
    }   
    
    public IBinder onBind(Intent intent) {
		return mBinder;
	}
    
	//Provide the interface to the activity.
    protected final TrackingInterface.Stub mBinder = new TrackingInterface.Stub() {
		public double getLatitude() throws RemoteException {
			return latitude;
		}
		public double getLongitude() throws RemoteException {
			return longitude;
		}
		public double getDistance() throws RemoteException {
			return distance;
		}
		public double getAverageSpeed() throws RemoteException {
			return averageSpeed;
		}
		public double getRatio() throws RemoteException {
			return ratio;
		}
		public double getRemoteLatitude()  {
			System.out.println("Insdie getRemoteLat"+remotelatitude);
			return remotelatitude;
		}	
		public double getRemoteLongitude() {
			
			return remoteLongitude;
		}
		public void queryServer(){
			TCPGet = new TCPQueryThread();
			TCPGet.start();	
		}
    };
    
    public class UpdatedSubscriber extends ServiceGPS.Subscriber{
    	
		//Called when the location has changed. Take a look at super (ServiceGPS) if is needed (!)
	public void onLocationChanged(Location location) {
		super.onLocationChanged(location);
		totalUpdates++;		
		linearDeadReckoning(location, lastLocation2);
		lastLocation2 = location;
		updateUI();	
		}
	}
    
    public void updateUI(){
    	Intent broadcastReceiverIntent = new Intent();
        broadcastReceiverIntent.setAction(P4_ClientTrackingActivity.BROADCAST_ACTION);
		broadcastReceiverIntent.putExtra(P4_ClientTrackingActivity.BROADCAST_TYPE, P4_ClientTrackingActivity.UPDATE_UI);
        sendBroadcast(broadcastReceiverIntent);
        //will send a broadcast to the Activity, which will then update the UI.
    }
    
      
    public void linearDeadReckoning(Location location, Location lastLocation){    	 
    	    	
    	if (updateIsNeeded(location)){
    	
    		if(lastLocation == null) // We've acquired the first location values by our GPS sensor
      			vectorPredicted = new Vector(0,0);
      		else   			
      			vectorPredicted = new Vector(location, lastLocation);    		
      		
      		latitudPredicted = location.getLatitude() + vectorPredicted.getX(); //we compute the next predicted position just for test and see the serverside.gpx
  			longitudPredicted = location.getLongitude() + vectorPredicted.getY();
  			lastLatitudPredicted = latitudPredicted;
  			lastLongitudPredicted = longitudPredicted;
  			
  			//Set all the values to sent to server using UDP
			ClientData 	cData = new ClientData();
			cData.setClientID(deviceId);
			cData.setLatitude(location.getLatitude());
			cData.setLongitude( location.getLongitude());	
			cData.setPredictionVectorX(vectorPredicted.getX());
			cData.setPredictionVectorY(vectorPredicted.getY());
			cData.setTimeStamp(datePositionWasAcquired.getTime());	
			System.out.println(cData.getClientID());
			System.out.println("Sending Data to the server");
			//Sending values to server using UDP
			mUDPHandler.send(PositionInfo.serializeData(cData));
			System.out.println("Sent");
			numberSentUpdates++;
    	}    	

    	Date date = new Date(); 		
    	serverSDCard.append("<trkpt lat=\"" + String.valueOf(location.getLatitude())+ "\"");
    	serverSDCard.append(" lon=\"" + String.valueOf(location.getLongitude()) + "\">\n");
    	serverSDCard.append("  <time>" + date.toString() + "</time>\n</trkpt>\n");  
    	 	
    	ratio = 1 - (numberSentUpdates/totalUpdates); //better if the result is near 1
    }
    
    
    
    private boolean updateIsNeeded(Location location) {		
    	
		if (lastLocation2 == null) { // We've acquired the first location values by our GPS sensor
			return true;						
		}
			
		latitudPredicted = lastLatitudPredicted + vectorPredicted.getX(); //we compute the next predicted position
		longitudPredicted = lastLongitudPredicted + vectorPredicted.getY();	
		lastLatitudPredicted = latitudPredicted;
		lastLongitudPredicted = longitudPredicted;		

		float[] resultsWorstcase = new float[3]; //check whether the next fix position can go beyond the THRESHOLD
		Location.distanceBetween(location.getLatitude(), location.getLongitude(),
				latitudPredicted, longitudPredicted, resultsWorstcase);
		
		if ((resultsWorstcase[0] + MAX_WALKING_SPEED) > THRESHOLD)
			return true;
		else 			
			return false;
		
	}
    
    
    //This TCP Thread will make a connection with the server using TCP IP
    //Query the server 
    //Receive Data from the server
    private class TCPQueryThread extends Thread{		
		public void run(){
			
					try{	
						//TCP Client Sock Object for Communication					
						TCPClientSocket = new Socket (address, TCPServerPortID);
						System.out.println("Connect to the server");
						TCPOutStream = TCPClientSocket.getOutputStream();
						
						TCPInStream = TCPClientSocket.getInputStream();
						//Client Data Class object
						ClientData clientInfo= new ClientData();
						clientInfo.setClientID(deviceId);
						//Serializing the Client Data Object 
						TCPByteStream = PositionInfo.serializeData(clientInfo);					

						//TCPOutStream.write(TCPByteStream);		
						System.out.println("Connected to the server on Port"+ TCPServerPortID );
						//Wiring to the server 
						TCPOutStream.write(TCPByteStream);		
						System.out.println("Send device ID to the server");
						TCPByteStream = new byte[2000];
						//Reading response from the server
						TCPInStream.read(TCPByteStream);						
						String receiveData = new String(TCPByteStream);
						System.out.println("Receive data from server");
						//Deserialing the response from the server.
						ClientData cData = 	PositionInfo.deserializeData(receiveData);				
						remotelatitude=cData.getLatitude();
						remoteLongitude=cData.getLongitude();
						Intent broadcastReceiverIntent = new Intent();
						broadcastReceiverIntent.putExtra(P4_ClientTrackingActivity.BROADCAST_TYPE, P4_ClientTrackingActivity.UPDATE_REMOTE_VALUES);
				        broadcastReceiverIntent.setAction(P4_ClientTrackingActivity.BROADCAST_ACTION);
				        sendBroadcast(broadcastReceiverIntent);
						// Close TCP I/O and socket
						TCPOutStream.close();
						TCPInStream.close();
						TCPClientSocket.close();
						System.out.println("");
						
						
					}
					catch(IOException e){
						System.out.print("Error : "+e.getMessage());
						e.printStackTrace();
					}	
					
		}
	}
    
}
